#include "ComponentChaseBehavior.h"
#include "Components/ComponentCreature.h"
#include "Components/ComponentBody.h"
#include "Components/ComponentCreatureSounds.h"
#include "Components/ComponentHealth.h"
#include "Components/ComponentAttacker.h"
#include "Components/ComponentCreatureModel.h"
#include "Components/ComponentLocomotion.h"
#include "ComponentRandomFeedBehavior.h"


#include "ComponentPathfinding.h"
#include "Managers/GameManager.h"

#include "Subsystems/SubsystemBodies.h"
#include "Subsystems/SubsystemGameInfo.h"
#include "Subsystems/SubsystemTimeOfDay.h"
#include "Subsystems/SubsystemWeather.h"
#include "SubsystemNoise.h"

#include "GameRandom.hpp"
#include "GameSingleton.h"
#include "GameRegistry.hpp"
#include "GameThreadPool.hpp"
#include "Game/BodyRaycastResult.hpp"


namespace PocketSurvival
{

    // ************** 【状态机】 **************

    ComponentChaseBehavior::StateMachine::StateMachine(ComponentChaseBehavior *chaseBehavior) : m_chaseBehavior(chaseBehavior), EasyMachine(MachineState::LookingForTarget) 
    {
        enter();
    }

    void ComponentChaseBehavior::StateMachine::enter()
    {
        switch (m_currentState)
        {
        case MachineState::LookingForTarget:
            m_chaseBehavior->m_importanceLevel = 0.0f;
            m_chaseBehavior->targetEntity = entt::null;
            break;
        case MachineState::RandomMoving:
            {
                Vector3 position;
                m_chaseBehavior->componentCreature->componentBody->getPosition(position);
                position += Vector3(GameSingleton::gameRandom->getFloat(-1.0f, 1.0f) * 6.0f, 0.0f, GameSingleton::gameRandom->getFloat(-1.0f, 1.0f) * 6.0f);

                m_chaseBehavior->componentPathfinding->setDestination(position, 1.0f, 1.0f, 0, false, true, false, nullptr);
            }
            break;
        case MachineState::Chasing:
            m_chaseBehavior->subsystemNoise->makeNoise(m_chaseBehavior->componentCreature->componentBody, 0.25f, 6.0f);
            // 播放音乐， 未完成...
            m_chaseBehavior->m_nextUpdateTime = 0.0;
            break;
        }
    }

    void ComponentChaseBehavior::StateMachine::update(float dt)
    {
        switch (m_currentState)
        {
        case MachineState::LookingForTarget:
            if (m_chaseBehavior->isActive())
            {
                changeTo(MachineState::Chasing);
            }
            else if (m_chaseBehavior->autoChaseSuppressionTime <= 0.0f && m_chaseBehavior->componentCreature->componentHealth->health > 0.4f)
            {
                ComponentCreature *targetCreature = nullptr;
                if (m_chaseBehavior->targetEntity != entt::null)
                {
                    if (GameSingleton::gameRegistry->valid(m_chaseBehavior->targetEntity))
                    {
                        targetCreature = GameSingleton::gameRegistry->get<ComponentCreature*>(m_chaseBehavior->targetEntity);
                    }
                    else
                    {
                        m_chaseBehavior->targetEntity = entt::null;
                    }
                }

                if (targetCreature == nullptr || m_chaseBehavior->scoreTarget(targetCreature))
                {
                    m_chaseBehavior->range = (m_chaseBehavior->weather->skylightIntensity < 0.2f) ? m_chaseBehavior->nightChaseRange : m_chaseBehavior->dayChaseRange;
                    ComponentCreature *creature  = m_chaseBehavior->findTarget();
                    if (creature != nullptr)
                    {
                        m_chaseBehavior->targetInRangeTime += dt;
                    }
                    else
                    {
                        m_chaseBehavior->targetInRangeTime = 0.0f;
                    }
                    if (m_chaseBehavior->targetInRangeTime > 3.0f)
                    {
                        bool flag = m_chaseBehavior->weather->skylightIntensity >= 1.0f;
                        float maxRange = flag ? (m_chaseBehavior->dayChaseRange + 6.0f) : (m_chaseBehavior->nightChaseRange + 6.0f);
                        float maxChaseTime = flag ? (m_chaseBehavior->dayChaseTime * GameSingleton::gameRandom->getFloat(0.75f, 1.0f)) : (m_chaseBehavior->nightChaseTime * GameSingleton::gameRandom->getFloat(0.75f, 1.0f));
                        m_chaseBehavior->attack(creature->entity, maxRange, maxChaseTime, !flag);
                    }
                }
            }
            break;
        case MachineState::RandomMoving:
            if (m_chaseBehavior->componentPathfinding->isStuck || m_chaseBehavior->componentPathfinding->destination == FastVector3::Zero)
            {
                changeTo(MachineState::Chasing);
            }
            if (m_chaseBehavior->isActive() == false)
            {
                changeTo(MachineState::LookingForTarget);
            }
            break;
        case MachineState::Chasing:
            if (m_chaseBehavior->isActive() == false)
            {
                changeTo(MachineState::LookingForTarget);
            }
            else if (m_chaseBehavior->chaseTime <= 0.0f)
            {
                m_chaseBehavior->autoChaseSuppressionTime = GameSingleton::gameRandom->getFloat(10.0f, 60.0f);
                m_chaseBehavior->m_importanceLevel = 0.0f;
            }
            else if (m_chaseBehavior->targetEntity == entt::null)
            {
                m_chaseBehavior->m_importanceLevel = 0.0f;
            }
            else if (GameSingleton::gameRegistry->valid(m_chaseBehavior->targetEntity))
            {
                ComponentCreature *targetCreature = GameSingleton::gameRegistry->get<ComponentCreature*>(m_chaseBehavior->targetEntity);
                if (targetCreature->componentHealth->health <= 0.0f)
                {
                    if (m_chaseBehavior->feedBehavior != nullptr)
                    {
                        Vector3 position;
                        targetCreature->componentBody->getPosition(position);

                        m_chaseBehavior->feedBehavior->feed(position);
                    }
                    m_chaseBehavior->m_importanceLevel = 0.0f;
                }
                else if (m_chaseBehavior->isPersistent == false && m_chaseBehavior->componentPathfinding->isStuck)
                {
                    m_chaseBehavior->m_importanceLevel = 0.0f;
                }
                else if (m_chaseBehavior->isPersistent && m_chaseBehavior->componentPathfinding->isStuck)
                {
                    changeTo(MachineState::RandomMoving);
                }
                else
                {
                    if (m_chaseBehavior->scoreTarget(targetCreature) <= 0.0f)
                    {
                        m_chaseBehavior->targetUnsuitableTime += dt;
                    }
                    else
                    {
                        m_chaseBehavior->targetUnsuitableTime = 0.0f;
                    }
                    if (m_chaseBehavior->targetUnsuitableTime > 3.0f)
                    {
                        m_chaseBehavior->m_importanceLevel = 0.0f;
                    }
                    else
                    {
                        int32_t maxPathfindingPositions = 0;
                        if (m_chaseBehavior->isPersistent)
                        {
                            
                        }

                        BoundingBox boundingBox = m_chaseBehavior->componentCreature->componentBody->getBoundingBox();
                        BoundingBox boundingBox2 = targetCreature->componentBody->getBoundingBox();
                        Vector3 v = 0.5f * (boundingBox.Min + boundingBox.Max);
                        Vector3 vector = 0.5f * (boundingBox2.Min + boundingBox2.Max);
                        float num = Vector3::Distance(v, vector);
                        float num2 = (num < 4.0f ? 0.2f : 0.0f);

                        Vector3 velocity;
                        targetCreature->componentBody->getVelocity(velocity);

                        m_chaseBehavior->componentPathfinding->setDestination((vector + num2 * num * velocity), 1.0f, 1.5f, maxPathfindingPositions, true, false, true, targetCreature->componentBody);
                        if (GameSingleton::gameRandom->getFloat(0.0f, 1.0f) < 0.33f * dt)
                        {
                            // 播放攻击声音
                        }
                    }
                }
            }
            else
            {
                m_chaseBehavior->targetEntity = entt::null;
                m_chaseBehavior->m_importanceLevel = 0.0f;
            }
            break;
        }
    }

    void ComponentChaseBehavior::StateMachine::leave()
    {
        switch (m_currentState)
        {
        case MachineState::LookingForTarget:
            break;
        case MachineState::RandomMoving:
            break;
        case MachineState::Chasing:
            break;
        }
    }

    // **************************************


    ComponentChaseBehavior::~ComponentChaseBehavior()
    {
        delete m_stateMachine;
    }

    float ComponentChaseBehavior::getImportanceLevel()
    {
        return m_importanceLevel;
    }

    void ComponentChaseBehavior::attack(entt::entity entity, float maxRange, float maxChaseTime, bool persistent)
    {
        targetEntity = entity;
        m_nextUpdateTime = 0.0;
        range = maxRange;
        chaseTime = maxChaseTime;
        isPersistent = persistent;
        m_importanceLevel = 200.0f;
    }


    ComponentCreature* ComponentChaseBehavior::findTarget()
    {
        Vector3 position;
        componentCreature->componentBody->getPosition(position);

        ComponentCreature *result = nullptr;
        float num = 0.0f;

        m_componentBodies.clear();
        subsystemBodies->findBodiesAroundPoint(Vector2(position.posX, position.posZ), range, m_componentBodies);
        
        for (ComponentBody *body : m_componentBodies)
        {
            if (body->componentCreature != nullptr)
            {
                float num2 = scoreTarget(body->componentCreature);
                if (num2 > num)
                {
                    num = num2;
                    result = body->componentCreature;
                }
            }
        }
        return nullptr;
    }


    float ComponentChaseBehavior::scoreTarget(ComponentCreature* creature)
    {
        bool flag = (creature->componentBody->componentPlayer != nullptr);
        bool flag2 = (creature->category != CreatureCategory::WaterPredator && creature->category != CreatureCategory::WaterOther);
        bool flag3 = (creature->entity == targetEntity || subsystemGameInfo->gameMode > GameMode::Harmless);
        bool flag4 = ((uint8_t)creature->category & (uint8_t)autoChaseMask) != 0;
        bool flag5 = (creature->entity == targetEntity || (flag4 && MathUtils::Remainder(0.005 + timeOfDay->getGameTime(), 1.0) < chaseNonPlayerProbability));
        if (creature != componentCreature && ((flag == false && flag5) || (flag && flag3)) && componentCreature->componentHealth->health > 0.0f && (flag2 || isTargetInWater(creature->componentBody)))
        {
            Vector3 position;
            Vector3 targetPosition;
            componentCreature->componentBody->getPosition(position);
            creature->componentBody->getPosition(targetPosition);

            float num = Vector3::Distance(position, targetPosition);
            if (num < range)
            {
                return range - num;
            }
        }
        return 0.0f;
    }

    
    bool ComponentChaseBehavior::isTargetInWater(ComponentBody *targetBody)
    {
        if (targetBody->immersionDepth > 0.0f)
        {
            return true;
        }
        // if (target->parentBody != nullptr && isTargetInWater(target->parentBody))
        // {
        //     return true;
        // }

        Vector3 targetPosition;
        Vector3 standingPosition;
        targetBody->getPosition(targetPosition);
        targetBody->standingOnBody->getPosition(standingPosition);

        if (targetBody->standingOnBody != nullptr && standingPosition.posY < targetPosition.posY && isTargetInWater(targetBody->standingOnBody))
        {
            return true;
        }
        return false;
    }

    bool ComponentChaseBehavior::isTargetInAttackRange(ComponentBody *targetBody)
    {
        if (isBodyInAttackRange(targetBody))
        {
            return true;
        }
        BoundingBox boundingBox = componentCreature->componentBody->getBoundingBox();
        BoundingBox boundingBox2 = targetBody->getBoundingBox();
        Vector3 v = 0.5f * (boundingBox.Min + boundingBox.Max);
        Vector3 v2 = 0.5f * (boundingBox2.Min + boundingBox2.Max) - v;
        float num = v2.length();
        Vector3 v3 = v2 / num;
        float num2 = 0.5f * (boundingBox.Max.posX - boundingBox.Min.posX + boundingBox2.Max.posX - boundingBox2.Min.posX);
        float num3 = 0.5f * (boundingBox.Max.posY - boundingBox.Min.posY + boundingBox2.Max.posY - boundingBox2.Min.posY);
        if (MathUtils::Abs(v2.posY) < num3 * 0.99f)
        {
            if (num < num2 && Vector3::Dot(v3, componentCreature->componentBody->getMatrix().getForward()) > 0.25f)
            {
                return true;
            }
        }
        else if (num < num3 + 0.3f && MathUtils::Abs(Vector3::Dot(v3, FastVector3::UnitY)) > 0.8f)
        {
            return true;
        }
        // if (target->parentBody != nullptr && isTargetInAttackRange(target->parentBody))
        // {
        //     return true;
        // }

        Vector3 targetPosition;
        Vector3 standingPosition;
        targetBody->getPosition(targetPosition);
        targetBody->standingOnBody->getPosition(standingPosition);

        if (targetBody->standingOnBody != nullptr && standingPosition.posY < targetPosition.posY && isTargetInAttackRange(targetBody->standingOnBody))
        {
            return true;
        }
        return false;
    }

    bool ComponentChaseBehavior::isBodyInAttackRange(ComponentBody *targetBody)
    {
        BoundingBox boundingBox = componentCreature->componentBody->getBoundingBox();
        BoundingBox boundingBox2 = targetBody->getBoundingBox();
        Vector3 v = 0.5f * (boundingBox.Min + boundingBox.Max);
        Vector3 v2 = 0.5f * (boundingBox2.Min + boundingBox2.Max) - v;
        float num = v2.length();
        Vector3 v3 = v2 / num;
        float num2 = 0.5f * (boundingBox.Max.posX - boundingBox.Min.posX + boundingBox2.Max.posX - boundingBox2.Min.posX);
        float num3 = 0.5f * (boundingBox.Max.posY - boundingBox.Min.posY + boundingBox2.Max.posY - boundingBox2.Min.posY);
        if (MathUtils::Abs(v2.posY) < num3 * 0.99f)
        {
            if (num < num2 + 0.99f && Vector3::Dot(v3, componentCreature->componentBody->getMatrix().getForward()) > 0.25f)
            {
                return true;
            }
        }
        else if (num < num3 + 0.3f && MathUtils::Abs(Vector3::Dot(v3, FastVector3::UnitY)) > 0.8f)
        {
            return true;
        }
        return false;
    }

    ComponentBody *ComponentChaseBehavior::getHitBody(ComponentBody *targetBody, Vector3 &outHitPoint)
    {
        Vector3 vector = componentCreature->componentBody->getBoundingBox().center();
        Vector3 v = targetBody->getBoundingBox().center();
        Ray3 ray(vector, Vector3::Normalize(v - vector));
        RaycastData raycastData = componentAttacker->raycast(ray, RaycastMode::Interaction);
        std::shared_ptr<BodyRaycastResult> bodyRaycastResult = raycastData.bodyRaycastResult;
        if (bodyRaycastResult != nullptr && 
            bodyRaycastResult->distance < 1.75f && 
                (bodyRaycastResult->componentBody == targetBody || 
                //  bodyRaycastResult->componentBody.isChildOfEntity(targetEntity) ||
                //  targetBody->isChildOfBody()
                 targetBody->standingOnBody == bodyRaycastResult->componentBody))
        {
            outHitPoint = bodyRaycastResult->hitPoint();
            return bodyRaycastResult->componentBody;
        }
        outHitPoint = FastVector3::Zero;
        return nullptr;
    }


    void ComponentChaseBehavior::update(float dt)
    {
        autoChaseSuppressionTime -= dt;
        if (isActive() && targetEntity != entt::null)
        {
            if (GameSingleton::gameRegistry->valid(targetEntity))
            {
                ComponentCreature *targetCreature = GameSingleton::gameRegistry->get<ComponentCreature*>(targetEntity);
                chaseTime -= dt;
                componentCreature->componentCreatureModel->lookAtOrder = std::make_shared<Vector3>(targetCreature->componentCreatureModel->getEyePosition());
                if (isTargetInAttackRange(targetCreature->componentBody))
                {
                    m_isAttack = true;
                }
                else
                {
                    m_isAttack = false;
                }
                // 发数据包
                if (m_isAttack == true)
                {
                    float attackPhase = m_attackPhase;
                    m_attackPhase = MathUtils::Remainder(m_attackPhase + dt * 2.0f, 1.0f);
                    if (attackPhase < 0.5f && m_attackPhase >= 0.5f)
                    {
                        Vector3 hitPoint;
                        ComponentBody *hitBody = getHitBody(targetCreature->componentBody, hitPoint);
                        if (hitBody != nullptr)
                        {
                            float x = isPersistent ? GameSingleton::gameRandom->getFloat(8.0f, 10.0f) : 2.0f;
                            chaseTime = MathUtils::Max(chaseTime, x);
                            componentAttacker->hitBody(hitBody, hitPoint, componentCreature->componentBody->getMatrix().getForward());
                            // 播放音乐
                            // 待完成 ...
                        }
                    }
                }
                else
                {
                    if (m_attackPhase > 0.5f)
                    {
                        m_attackPhase = MathUtils::Remainder(MathUtils::Min(m_attackPhase + dt * 2.0f, 1.0f), 1.0f);
                    }
                    else if (m_attackPhase > 0.0f)
                    {
                        m_attackPhase = MathUtils::Max(m_attackPhase - dt * 2.0f, 0.0f);
                    }
                }
            }
            else
            {
                targetEntity = entt::null;
            }
        }
        if (timeOfDay->getGameTime() >= m_nextUpdateTime)
        {
            float theDt = GameSingleton::gameRandom->getFloat(0.25f, 0.35f) + MathUtils::Min((float)(timeOfDay->getGameTime() - m_nextUpdateTime), 0.1f);
            m_nextUpdateTime = timeOfDay->getGameTime() + theDt;
            m_stateMachine->update(theDt);
        }
    }

    static const std::string componentName = "ChaseBehavior";
    const std::string &ComponentChaseBehavior::getName() const
    {
        return componentName;
    }
    const std::string &ComponentChaseBehavior::GetName()
    {
        return componentName;
    }
    void ComponentChaseBehavior::load(const nlohmann::json &jsonData)
    {
        componentCreature = GameSingleton::gameRegistry->get<ComponentCreature*>(entity);
        componentPathfinding = GameSingleton::gameRegistry->get<ComponentPathfinding*>(entity);
        componentAttacker = GameSingleton::gameRegistry->get<ComponentAttacker*>(entity);
        ComponentRandomFeedBehavior **feedBehaviorPtr = GameSingleton::gameRegistry->try_get<ComponentRandomFeedBehavior*>(entity);
        if (feedBehaviorPtr != nullptr)
        {
            feedBehavior = *feedBehaviorPtr;
        }
        else
        {
            feedBehavior = nullptr;
        }
        


        subsystemBodies = GameSingleton::singletonPtr->getSingleton<SubsystemBodies>();
        subsystemGameInfo = GameSingleton::singletonPtr->getSingleton<SubsystemGameInfo>();
        timeOfDay = GameSingleton::singletonPtr->getSingleton<SubsystemTimeOfDay>();
        weather = GameSingleton::singletonPtr->getSingleton<SubsystemWeather>();
        subsystemNoise = GameSingleton::singletonPtr->getSingleton<SubsystemNoise>();

        dayChaseRange = jsonData["DayChaseRange"].get<float>();
        nightChaseRange = jsonData["NightChaseRange"].get<float>();
        dayChaseTime = jsonData["DayChaseTime"].get<float>();
        nightChaseTime = jsonData["NightChaseTime"].get<float>();
        autoChaseMask = jsonData["AutoChaseMask"].get<uint8_t>();
        chaseNonPlayerProbability = jsonData["ChaseNonPlayerProbability"].get<float>();
        chaseWhenAttackedProbability = jsonData["ChaseWhenAttackedProbability"].get<float>();
        chaseOnTouchProbability = jsonData["ChaseOnTouchProbability"].get<float>();

        componentCreature->componentHealth->addAttackedEvent([this](entt::entity attacker)
        {
            if (GameSingleton::gameRandom->getFloat(0.0f, 1.0f) < chaseWhenAttackedProbability)
            {
                if (chaseWhenAttackedProbability >= 1.0f)
                {
                    attack(attacker, 30.0f, 60.0f, false);
                }
                else
                {
                    attack(attacker, 7.0f, 7.0f, false);
                }
            }
        });

        componentCreature->componentBody->addCollidedWithBodyEvent([this](ComponentBody *body)
        {
            if (targetEntity == entt::null && autoChaseSuppressionTime <= 0.0f && GameSingleton::gameRandom->getFloat(0.0f, 1.0f) < chaseOnTouchProbability)
            {
                ComponentCreature *bodyCreature = body->componentCreature;
                if (bodyCreature != nullptr)
                {
                    bool flag2 = (body->componentPlayer != nullptr);
                    bool flag3 = ((uint8_t)bodyCreature->category & autoChaseMask) != 0;
                    if ((flag2 && subsystemGameInfo->gameMode > GameMode::Harmless) || (!flag2 && flag3))
                    {
                        attack(bodyCreature->entity, 7.0f, 7.0f, false);
                    }
                }
            }
            if (targetEntity != entt::null && body->entity == targetEntity && body->standingOnBody == componentCreature->componentBody)
            {
                componentCreature->componentLocomotion->setJumpOrder(1.0f);
            }
        });



        m_stateMachine = new StateMachine(this);
    }

}

